package engine
{
	import bitmap.ImageDecoder;
	
	import com.funcity.mmo.generic.scene2d.TileLayerEvent;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.ProgressEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.getTimer;
	
	import loaders.MapLoader;
	
	import timer.TimerClock;

	/**
	 * 处理Tile base的地图。
	 * 将加载过的Tile做一个位图缓存。
	 * 可以不做缓存，CPU占用差别并不明显，推荐不缓存
	 * 内存占用：
	 * 1 不缓存情况下，预加载区域5，浏览器中稳定在50M以内
	 * 2 缓存情况，根据地图总大小，10000*10000像素约需要400M空间。
	 *  
	 * @author Ryan
	 * 
	 */	
	public class TileLayer extends Sprite
	{
		public static var instance:TileLayer;
		
		// 测试用，可以关闭加载
		public var loadTiles:Boolean = true;
		/**
		 * 位图缓存开关，默认关
		 * 可由用户设置。关闭会导致走动一顿一顿。测试效率时关闭。
		 */		
		public static var USE_CACHE : Boolean = true;
		public static var SHOW_NUM:int = 6;
		
		private static const EXTRA_BUFFER_LENGTH:int = 30;
		/**
		 * 推荐使用200*120
		 * 整个场景5*5，减少http请求数。 
		 */		
		public static var TILE_WIDTH:int = 500;//200;
		public static var TILE_HEIGHT:int = 300;//120;
		// 这个数字越小，加载速度越快
		public static var PRE_LOAD_AREA:int = 1;
		
		public static var onMiniMapLoaded:Function = null;
		public static var onLoadProgress:Function = null;
		
		/**
		 * 保存tile的loader表。 
		 */		
		private var _tileTable : Object;
		/**
		 * 根据url缓存tile的bitmapData 
		 */		
		private var _tileCache : Object;
		private var _tileFlag : Object;
		private var _mapClient:MapLoader;
		private var _mapId : int;
		private var _mapData:Object;
		/**
		 * tile个数 
		 */		
		private var _width :int;
		private var _height:int;
		
		private var _px:int;
		private var _py:int;
		
		private var _preX : int;
		private var _preY : int;
		private var _pixX : int;
		private var _pixY : int;
		
		private var _mapDecoder : ImageDecoder;
		private var _decoder : ImageDecoder;
		private var loader:URLLoader;
		private var miniMap:Bitmap;
		private var minimapURL:String;
		private var loadedCounter:int = 0;
		private var _folderURL : String = null;
		
		private var _minimapLoading : uint = 0;
		
		private var _halfVisibleWidth : uint = 0;
		private var _halfVisibleHeight : uint = 0;
		
		public function TileLayer()
		{
			_tileTable = {};
			_tileCache = {};
			_mapClient = new MapLoader();
			_mapClient.onData = onData;
			_decoder = new ImageDecoder();
			_mapDecoder = new ImageDecoder();
			_mapId = -1;
			_preX = -1;
			_preY = -1;
			
			loader = new URLLoader();
			loader.dataFormat = URLLoaderDataFormat.BINARY;
			loader.addEventListener( ProgressEvent.PROGRESS, _onLoadProgress );
			miniMap = new Bitmap();
			addChild( miniMap );
			
			instance = this;
			
			mouseEnabled = false;
			mouseChildren = false;
		}
		
		private function _onLoadProgress( event : ProgressEvent ):void
		{
			if ( onLoadProgress != null )
			{
				var percent:int = 100 * event.bytesLoaded / event.bytesTotal;
				onLoadProgress( percent );
			}
		}
		
		public function setMap( mid : int, pixX:int, pixY :int, pixWidth : int , pixHeight : int, folderURL : String ) : void {
			clearAll();
			
			_folderURL = folderURL;
			_mapId = mid;
			_width = pixWidth/TILE_WIDTH;
			_height = pixHeight/TILE_HEIGHT;
			_px = pixWidth;
			_py = pixHeight;
			
			setBackground();
			setCenterPix( pixX, pixY );
		}
		
		private function setBackground():void 
		{		
			loader.addEventListener(Event.COMPLETE,loadCompleteHandler);
			_minimapLoading = getTimer();
			loader.load(new URLRequest(_folderURL + "mini.jpg")); // 加载一个略缩图，放置真实地图下面，真实地图加载进来后会盖在上面
		}
		
		private function loadCompleteHandler(event:Event):void {
			loader.removeEventListener(Event.COMPLETE,loadCompleteHandler);
			
			//BaseMap.setScale( _px, _py );
			miniMap.width = _px;
			miniMap.height = _py;
			_mapDecoder.decode(loader.data,miniMap,miniCallBack);
		}
		
		private function miniCallBack(bitmapData:BitmapData):void{
			_minimapLoading = 0;
			
			//BaseMap.callBack(bitmapData);
			if ( onMiniMapLoaded != null )
			{
				onMiniMapLoaded(bitmapData);
			}
			
			setCenter( _preX, _preY );
			
			miniMap.width = _px;
			miniMap.height = _py;
			// MMOGame.net.call("onChangedScene", null);
		}
		
		public function setCenterPix( x : Number, y : Number , offx:Number = 0 , offy:Number = 0 ) : void {
			x += offx;
			y += offy;
			
			var dx:int = x/TILE_WIDTH;
			var dy:int = y/TILE_HEIGHT;
			
			_pixX = x;
			_pixY = y;
			
			if ( _preX == dx && _preY == dy )
			{
				return;
			}
			if ( !loadTiles )
			{
				return;
			}
			
			if ( _minimapLoading > 0 )
			{
				// 
				if ( ( getTimer() - _minimapLoading ) < 3000 )
				{
					_preX = dx;
					_preY = dy;
					return;
				}
				
				_minimapLoading = 0;
			}
			
			setCenter( dx, dy );
		}
		
		/**
		 * 设置tileLayer的中心位置
		 * 加载周边区域。 
		 * 根据地图大小考虑是否要释放远离视野范围的视图。
		 * unload也会带来消耗。
		 * @param x
		 * @param y
		 * 
		 */		
		private function setCenter( tileX : int , tileY : int ) : void {
			
			// 标记使用到的tile
			var localtileFlag:Object =  _tileFlag;
			_tileFlag = {};
			_preX = tileX;
			_preY = tileY;
			var x1:int = (_pixX - _halfVisibleWidth - EXTRA_BUFFER_LENGTH) / TILE_WIDTH;
			var y1:int = (_pixY - _halfVisibleHeight - EXTRA_BUFFER_LENGTH) / TILE_HEIGHT;
			var x2:int = (_pixX + _halfVisibleWidth + EXTRA_BUFFER_LENGTH) / TILE_WIDTH;
			var y2:int = (_pixY + _halfVisibleHeight + EXTRA_BUFFER_LENGTH) / TILE_HEIGHT;
			// 这里处理在地图边缘的情况，将调整加载区域 
			if( x1 < 0 ) {
				x1 = 0;
				if (x2 < _width) x2 += 1;
			}
			if( y1 < 0 ) {
				y1 = 0;
				if( y2 < _height) y2 += 1;	
			}
			if( x2 > _width ) {
				if (x1 > 0) x1 -= 1;
				x2 = _width;
			}
			if( y2 > _height) {
				if (y1 > 0) y1 -= 1;
				y2 = _height
			}
			for( var i :int = x1; i <= x2; i ++ ){
				for( var j : int = y1; j <= y2; j ++ ){
					if (localtileFlag) {
						if (localtileFlag[i + "_" + j]) {
							_tileFlag[i + "_" + j] = true;
						} else {
							loadTile( i, j );
						}
					} else {
						loadTile( i, j );
					}
				}
			}
			// 考虑是否放在回调中做 clear 没使用过的Tile
			clearUnused();
		}
		
		private var _timer:TimerClock = TimerClock.getInstance();
		private var _eventHandler:uint = 0;
		
		private function sendFinishEvent():void{
			dispatchEvent(new TileLayerEvent( TileLayerEvent.ENTER_MAP_FINISH ));
		}
		
		/**
		 * 这个函数中，可以增加异步队列，将优先级降低。 
		 * @param obj
		 * 
		 */		
        private function onData( obj :Object ) : void {
			processData( obj );
			// _timer.setTimeout( processData,10,obj );
        }
		
		/**
		 * 实际处理位图信息 
		 * @param obj
		 * 
		 */		
		private function processData( obj :Object ):void{
			
			// 判断是否是进入地图后的初始加载
			if( loadedCounter ++ == SHOW_NUM ){
				// 因为有异步处理，所以可能需要一个延迟
				if( _eventHandler > 0 ){
					_timer.remove( _eventHandler );
				}
				// 暂时延迟3秒。
				_eventHandler = _timer.setTimeout( sendFinishEvent, 1000 );
			}
			var bmp : Bitmap = getBitmap( obj.x, obj.y );
			// 重复加载的，不再解码。
			if( bmp.bitmapData ){
				return;
			}
			
			var s:Number = getTimer();
			// 这个操作是异步的。
			_decoder.decode( obj.data, bmp );
			if(getTimer() - s)trace( "decode Data:",getTimer() - s );
			
		}
        
        /**
         * 根据x，y加载一个tile，如果已经加载过，则不再加载。 
         * @param x
         * @param y
         * 
         */        
        private function loadTile( x : int, y : int ) : void {
        	// 标记使用状态 
			if( !_tileFlag ) _tileFlag = {};
			if( _tileFlag[ x + "_" + y ] ) return;
			
            _tileFlag[ x + "_" + y ] = true;
            
        	var bmp : Bitmap = getBitmap( x, y );
			
    		if( bmp.bitmapData ){
    			// 已经加载。在过图时会清空所有Loader
    			return;
    		}
    		// 判断内存中是否有缓存
    		var url : String = _folderURL + y + "/" + x + ".jpg";
    		if( _tileCache[ url ] ){
    			bmp.bitmapData = _tileCache[ url ];
    			return;
    		}
    		// 为了编辑方便，正式输出可以用x_y的形式。
    		var loadInfo :Object = {};
		//loadInfo.type = MapConnection.MSG_TYPE_LOAD;
        	loadInfo.url = url;
    		loadInfo.x = x;
            loadInfo.y = y;
			loadInfo.mid = _mapId;
            _mapClient.sendMessage( loadInfo );
        }
        
        /**
         * 根据位置得到一个loader ,如果没有会新创建，不会返回空。
         * @param x 实际坐标/TILE_WIDTH并取整过后的坐标。
         * @param y
         * @return 
         * 
         */        
        private function getBitmap( x : int , y : int ) : Bitmap {
        	var key : String = x + "_" + y;
        	if( _tileTable[ key ] ){
				// 这里重新加一次。
				addChild( _tileTable[ key ] as Bitmap );
        		return _tileTable[ key ];
        	}
        	var bmp :Bitmap = new Bitmap();
			bmp.smoothing = true;
        	bmp.x = x * TILE_WIDTH;
        	bmp.y = y * TILE_HEIGHT;
        	bmp.bitmapData = null;
        	_tileTable[ key ] = bmp;
        	addChild( bmp );
        	return bmp;
        }
        
		public function clearAll() : void
		{
			_tileFlag = {};
			for each( var bmp :Bitmap in _tileTable )
			{
				if( bmp.bitmapData )
				{
					bmp.bitmapData.dispose();
					bmp.bitmapData = null;
				}
			}
			loadedCounter = 0;
			// 切换地图时，清除地图位图缓存（使用浏览器缓存）。
			_mapClient.clearAll();
			
			_preX = -1;
			_preY = -1;
			
			if ( miniMap && miniMap.bitmapData )
			{
//				miniMap.bitmapData.dispose();
//				miniMap.bitmapData = null;
			}
		}
        
        /**
         * 清除所有未使用的tile 
         * 
         */        
        private function clearUnused() : void {
        	for ( var key :String in _tileTable ){
        		if( !_tileFlag[ key ] ){
        			var bmp : Bitmap = _tileTable[key];
					if( !USE_CACHE ){
        				bmp.bitmapData = null;
					}
					if( bmp.parent ){
						// 没有bitmapData的bmp也会绘制，所以要移除。
						bmp.parent.removeChild( bmp );
					}
        			// 不清除bmp对象
        		}
        	}
        }
		
		public function setVisibleSize( width:int, height:int ):void
		{
			_halfVisibleWidth = width * 0.5;
			_halfVisibleHeight = height * 0.5;
		}
	}
}